const kHandler = Symbol('handler')
const kListeners = Symbol('listeners')
const kOrigin = Symbol('origin')
const kOther = Symbol('other')

class PostMessageEventEmitter {
    constructor(other, origin) {
        if (typeof other.postMessage !== 'function') {
            throw new TypeError('Expected other.postMessage to be a function')
        }

        if (typeof origin !== 'string') {
            throw new TypeError('Expected origin to be a string')
        }

        this[kListeners] = {}
        this[kOrigin] = origin
        this[kOther] = other

        this[kHandler] = (ev) => {
            // Check origin of message
            if (ev.origin !== this[kOrigin]) return
            if (ev.source !== this[kOther]) return

            // Check message data
            if (ev.data === null) return
            if (typeof ev.data !== 'object') return
            if (typeof ev.data.name !== 'string') return

            // Emit event
            if (this[kListeners][ev.data.name]) {
                for (const fn of this[kListeners][ev.data.name]) {
                    Promise.resolve().then(() => fn(ev.data.name, ev.data.data))
                }
            }
        }

        window.addEventListener('message', this[kHandler])
    }

    on(name, fn) {
        if (typeof name !== 'string') throw new TypeError('Expected name to be a string')
        if (typeof fn !== 'function') throw new TypeError('Expected fn to be a function')

        if (this[kListeners][name]) {
            this[kListeners][name].push(fn)
        } else {
            this[kListeners][name] = [fn]
        }
    }

    emit(name, data) {
        if (typeof name !== 'string') throw new TypeError('Expected name to be a string')

        this[kOther].postMessage({name, data}, this[kOrigin])
    }

    stop() {
        window.removeEventListener('message', this[kHandler])
    }
}

module.exports = PostMessageEventEmitter
